package com.ssl.deadlock.service.impl;

import com.ssl.deadlock.dao.SealDao;
import com.ssl.deadlock.dao.UserDao;
import com.ssl.deadlock.dao.UserSealDao;
import com.ssl.deadlock.domain.Seal;
import com.ssl.deadlock.domain.User;
import com.ssl.deadlock.domain.UserSeal;
import com.ssl.deadlock.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * 描述：
 *
 * @author ssl
 * @create 2021/03/01 17:33
 */
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDao userDao;
    @Autowired
    private SealDao sealDao;
    @Autowired
    private UserSealDao userSealDao;


    /**
     * 无论user_seal表的user_id是否有加索引，都会产生死锁，操作流程：更新-插入
     * @param userId
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void createUserSeal(Long userId){
        userSealDao.updateDefaulted(Boolean.FALSE, userId);
        UserSeal userSeal = new UserSeal();
        userSeal.setSealId(0L);
        userSeal.setUserId(userId);
        userSeal.setDefaulted(Boolean.TRUE);
        userSealDao.save(userSeal);
    }

    /**
     * user_seal表的user_id没加索引，会产生死锁，加了索引则不会死锁,操作流程：插入-更新-更新
     * @param userId
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void createUserSeal2(Long userId){
        UserSeal userSeal = new UserSeal();
        userSeal.setSealId(0L);
        userSeal.setUserId(userId);
        userSeal.setDefaulted(Boolean.FALSE);
        userSealDao.save(userSeal);
        userSealDao.updateDefaulted(Boolean.FALSE, userId);
        userSeal.setDefaulted(Boolean.TRUE);
        userSealDao.save(userSeal);
    }

    /**
     * 创建用户、创建用户默认印章<br/>
     * Bugs:<br/>
     * 1、当执行userSealDao.updateDefaulted(Boolean.FALSE, user.getId())的时候，因为where条件没有索引，会将user_seal整个表锁住，
     *所以当执行到userSealDao.save(userSeal)的时候，会报 “Deadlock found when trying to get lock; try restarting transaction”
     *<br/>
     * 2、在1的基础上，对user_seal表的user_id加上索引，执行的时候一样会报上面的错误，原因是在当前事物中，操作的是同一个user_id。
     * @param username
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void create(String username) {
        // 1、创建用户
        User user = userDao.findByUsername(username);
        if (null == user) {
            user = new User();
        }
        user.setUsername(username);
        userDao.save(user);
        // 2、取消用户的所有默认印章
        userSealDao.updateDefaulted(Boolean.FALSE, user.getId());
        // 3、创建印章
        Seal seal = new Seal();
        seal.setImage("img");
        seal.setName(username);
        sealDao.save(seal);
        // 4、绑定印章为用户默认印章
        UserSeal userSeal = new UserSeal();
        userSeal.setUserId(user.getId());
        userSeal.setSealId(seal.getId());
        userSeal.setDefaulted(Boolean.TRUE);
        userSealDao.save(userSeal);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void create2(String username) {
        // 1、创建用户
        User user = userDao.findByUsername(username);
        if (null == user) {
            user = new User();
        }
        user.setUsername(username);
        userDao.save(user);
        // 2、创建印章
        Seal seal = new Seal();
        seal.setImage("img");
        seal.setName(username);
        sealDao.save(seal);
        // 3、绑定印章为用户默认印章
        UserSeal userSeal = new UserSeal();
        userSeal.setUserId(user.getId());
        userSeal.setSealId(seal.getId());
        userSeal.setDefaulted(Boolean.TRUE);
        userSealDao.save(userSeal);
        // 4、取消用户的所有默认印章
        userSealDao.updateDefaulted(Boolean.FALSE, user.getId());
        // 5、设置默认印章
        userSeal.setDefaulted(Boolean.TRUE);
        userSealDao.save(userSeal);
    }
}
